package com.jiang.books.common.advice;

import java.util.Collection;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//import org.aspectj.lang.annotation.AfterReturning;
//import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;

import com.jiang.books.domain.Author;
import com.jiang.books.domain.Book;
import com.jiang.books.domain.Country;
import com.jiang.books.service.AuthorService;
import com.jiang.books.service.BookService;
import com.jiang.books.service.CountryService;

@Component
//@Configurable
@Aspect
public class ServiceAdvice {
	
	private static final Logger log = Logger.getRootLogger();
    
    @Autowired
    private CountryService countryService;

    @Autowired
    private AuthorService authorService;

    @Autowired
    private BookService bookService;

    
    @Pointcut("execution(* com.jiang.books.service.*.save(..))")
    protected void saveExecution() {
    }

    @Pointcut("execution(* com.jiang.books.service.*.delete(..))")
    protected void deleteExecution() {
    }

    //@Pointcut("@annotation(com.jiang.books.utils.annotation.AdditionalLogging)")
    protected void logAnnotatedMethods() {
    }

    
    //@Around("logAnnotatedMethods()")
    public Object loggingAroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        
        Object[] args = pjp.getArgs();

        log.debug("Before execution: " + pjp.getSignature().getDeclaringTypeName() + " " 
                + pjp.getSignature().getName()
                + " argument: " + args);

        Object retVal = pjp.proceed();
        if (retVal instanceof Collection) {
        	
        	log.debug("[Result is instance of Collection] " +  pjp.getSignature().getName() + " - " + retVal);
        	
        }

        log.debug("After execution: " + pjp.getSignature().getDeclaringTypeName() + " " 
                + pjp.getSignature().getName()
                + " argument: " + args);        
        
        return retVal;
    }
    
    @Before("saveExecution()")
    public void saveObjectAdvice(JoinPoint joinPoint) throws Throwable {

        Object[] args = joinPoint.getArgs();

        log.debug("Executing: " + joinPoint.getSignature().getDeclaringTypeName() + " " 
                + joinPoint.getSignature().getName()
                + " argument: " + args);

        for (int x = 0; x < args.length; x++) {
            if (args[x] instanceof Author) {

                Author author = (Author) args[x];
                Country country = author.getCountry();
                
                countryService.save(country);
                log.debug("Save related Country - " + country);

            }
            
            if (args[x] instanceof Book) {

                Book book = (Book) args[x];
                Author author = book.getAuthor();
                
                authorService.save(author);
                log.debug("Save related Author - " + author);
                
            }
        }
    }
    
    @Before("deleteExecution()")
    public void deleteObjectAdvice(JoinPoint joinPoint) throws Throwable {

        Object[] args = joinPoint.getArgs();

        log.debug("Executing: " + joinPoint.getSignature().getDeclaringTypeName() + " " 
                + joinPoint.getSignature().getName()
                + " argument: " + args);

        for (int x = 0; x < args.length; x++) {
            if (args[x] instanceof Author) {

                Author author = (Author) args[x];
                author.setCountry(null);
                
                authorService.save(author);

            }
            
            if (args[x] instanceof Book) {

                Book book = (Book) args[x];
                book.setAuthor(null);
                
                bookService.save(book);
                
            }
        }
    }
	
}